# Copyright 1999-2012 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: $

#
# Original Author: Serge V. Zarviroff <zarviroff@gmail.com>
#
# Bugs, patches to zarviroff@gmail.com
#

# This eclass breaks the merge prior to src_unpack phase if there are any blocking package installed at the moment.
# However, this eclass is a helper to thexgames eclass and it works with a tracker_TRACKFILE provided by tracker.eclass
# not with portage's world file and db/pkg. It's not a pain to use those files to make it really universal, but i just don't
# see any sense to make it. A tracker_TRACKFILE is required anyway to deal game's addons properly.
# emerge.log could be parsed for that purpose off course. clns_STRICTORDER mark could be achieved by bzcat'ing env from
# db/pkg. But that'd take much more time than parsing a tracker_TRACKFILE. And as backups must be stored somewhere
# why not store a tracker_TRACKFILE there too?
# Moreover, as i can see - there is no frakkin need of this eclass' frakkin functionality with current portage version. :(
#
# AFAIK, there is no need to implement "Built with USE Dependencies" part as in that case portage will break the merge
# itself until the user make the reuired USE-change.

# This is russian:
#
# обработка сегмента:
# 	для каждого токена в сегменте
# 		обработка токена
# конец
# 
# обработка токена:
# 	если токен = ||
# 		обработка сегмента
# 	иначе если в конце токена - вопрос
# 		восклицак=0
# 		если в начале токена восклицак, восклицак=1
# 		[ токен включён И не восклицак ] ИЛИ [ токен не включён И восклицак ] : обработка сегмента
# 	иначе
# 		обработка имени
# конец
# 
# обработка имени:
# 	если первый символ - восклицак
# 		если дальше оператор сранения
# 			оператор = оператор
# 			версия = версия
# 			проверить
# 		иначе
# 			проверить
# конец
#
# :)
#
# xoxoxo

bob_handle_name() {
	local token="${1}"
	if [[ "${token:0:1}" == '!' ]]
	then
		local i
		local compare_operation
		
		if [[ "${token:1:1}" == "<" ]] || [[ "${token:1:1}" == ">" ]]
		then
			if [[ "${token:2:1}" == "=" ]]
			then
				compare_operation="${token:1:1}${token:2:1}"
			else
				compare_operation="${token:1:1}"
			fi
		elif [[ "${token:1:1}" == "=" ]]
		then
			compare_operation="${token:1:1}"
		fi

		# this is the token w/o ! < = symbols
		token="${token:$((${#compare_operation}+1))}"
		tokens_cat="${token%%/*}"
		tokens_pn="$(echo ${token#${tokens_cat}/} | sed 's|-[0-9].*$||')"
		
		local installed_atom=$(ls "${bob_DB}/${tokens_cat}" 2>/dev/null | grep ${tokens_pn})

		local active=0
		if [[ -n "${installed_atom}" ]]
		then
			installed_atom="${tokens_cat}/${installed_atom}"
			if [[ -z "${compare_operation}" ]]
			then
				active=1
			else
				if (( "${#compare_operation}" == 1 ))
				then
					eval "[[ ${installed_atom} ${compare_operation} ${token} ]] && active=1"
				else
					eval "if [[ ${installed_atom} ${compare_operation:0:1} ${token} ]] || [[ ${installed_atom} ${compare_operation:1:1} ${token} ]]; then active=1; fi"
				fi
			fi
		fi

		if (( "${active}" ))
		then
			local i
			local match=0
			for (( i=0; "${i}" < "${#bob_ACTIVEBLOCKES[@]}"; ++i ))
			do
				[[ "${installed_atom}" == "${bob_ACTIVEBLOCKES[${i}]}" ]] && match=1
			done

			if (( ! "${match}" ))
			then
				bob_ACTIVEBLOCKES[${#bob_ACTIVEBLOCKES[@]}]="${installed_atom}"
			fi
		fi
	fi
}

bob_segment_end() {
	local i
	local skip=0

	# this should be an infinite loop, but it'd be potentially real infinite
	# so let it consider the last-most dep as an absolutely total boundry, that shouln't ever be overstepped
	
	# the whole function starts with a '('. So this loop should start iteration from the next element
	for (( i="$((${1}+1))"; "${i}" < "${#bob_ALLDEPS[@]}"; ++i ))
	do
		# if a loop faces another '(', then it means it faced a nested segment
		[[ "${bob_ALLDEPS[${i}]}" == "(" ]] && (( ++skip ))
		if [[ "${bob_ALLDEPS[${i}]}" == ")" ]]
		then
			if (( "${skip}" ))
			then
				(( --skip )) && continue
			else
				echo "${i}" && return "${i}"
			fi
		fi
	done
}

bob_handle_token() {
	local token="${1}"

	if [[ "${token}" == '(' ]] || [[ "${token}" == ')' ]]
	then
		# there are no notices in the Gentoo Dev Manual that a token could _start_ from a '('
		# According to GDM it could be like "foo? ( blah )" or "|| ( foo bar )", etc. but not "( foo bar )"
		return
	fi
	if [[ "${token}" == "||" ]]
	then
		local segment_end=$(bob_segment_end $((${element}+1)))
		bob_handle_segment $((${element}+1)) ${segment_end}
	elif [[ "${token:$((${#token}-1)):1}" == "?" ]]
	then
		token="${token:0:$((${#token}-1))}"
		local segment_end=$(bob_segment_end $((${element}+1)))
		if use "${token}"
		then
			bob_handle_segment $((${element}+1)) ${segment_end}
		else
			element=${segment_end}
		fi
	else
		bob_handle_name "${token}"
	fi
}

bob_handle_segment() {
	local element
	for (( element="${1}"; "${element}" <= "${2}"; ++element ))
	do
		bob_handle_token "${bob_ALLDEPS[${element}]}"
	done
}

# Usage: bob_getBuildTime "cat/pf"
bob_getBuildTime() {
	local cat="${1%/*}"
	local pf="${1#*/}"
	local pn="$(echo ${pf} | sed 's|-[0-9].*$||')"
	local pvr="${pf#${pn}-}"
	
	local buildtime=""
	# Tracked by the tracker eclass build time priority is higher so checking for its presence at first
	if [[ -d "${bob_TR}" ]]
	then
		buildtime=$(find ${bob_TR} -type f -name "tBT=*:tCAT=${cat}:tPN=${pn}:tPVR=${pvr}*" -printf "%f\n" | sed 's/:tCAT=.*$//;s/^.*tBT=//')
	fi
	
	# If there are no trackfiles or if there are but the package being queried hasn't been tracked by the tracker eclass
	# then $buildtime' length will be = 0
	# In this case it queries /var/db/pkg
	if [[ -z "${buildtime}" ]]
	then
		buildtime="$(cat ${bob_DB}/${1}/BUILD_TIME)"
	fi
	
	echo "${buildtime}"
}

bob_rsort() {
	if (( "${#bob_ACTIVEBLOCKES[@]}" ))
	then
		local i ntime btime mtime lnmove=-1 tmp
		
		local cur_pos
		for (( cur_pos=0; "${cur_pos}" < $((${#bob_ACTIVEBLOCKES[@]}-1)); ++cur_pos ))
		do
			btime=$(bob_getBuildTime "${bob_ACTIVEBLOCKES[${cur_pos}]}")
			for (( i=$((${cur_pos}+1)); "${i}" < "${#bob_ACTIVEBLOCKES[@]}"; ++i ))
			do
				ntime=$(bob_getBuildTime "${bob_ACTIVEBLOCKES[${i}]}")
				if (( "${ntime}" > "${btime}" ))
				then
					btime=${ntime}
					lnmove=${i}
				fi
			done
			if (( "${lnmove}" != -1 ))
			then
				tmp="${bob_ACTIVEBLOCKES[${cur_pos}]}"
				bob_ACTIVEBLOCKES[${cur_pos}]="${bob_ACTIVEBLOCKES[${lnmove}]}"
				bob_ACTIVEBLOCKES[${lnmove}]="${tmp}"
				lnmove=-1
			fi
		done
	fi
}

bob_set_ACTIVEBLOCKERS() {
	local bob_ALLDEPS=(${DEPEND} ${RDEPEND})
	bob_handle_segment 0 $((${#bob_ALLDEPS[@]}-1))
	bob_rsort
}

# Usage: bob_bob dbpkgpath (trackdirpath)
bob_bob() {
	bob_DB="${1}"
	bob_TR="${2}"
	
	if [[ -d "${bob_DB}" ]]
	then
		bob_set_ACTIVEBLOCKERS
		if (( "${#bob_ACTIVEBLOCKES[@]}" > 0 ))
		then
			eerror ""
			eerror "This package can't be installed right now. As there is/are some"
			eerror "other package(s) blocking the install:"
			eerror ""
			for (( index=0; "${index}" < "${#bob_ACTIVEBLOCKES[@]}"; ++index ))
			do
				eerror "=${bob_ACTIVEBLOCKES[${index}]}"
			done
			eerror ""
			eerror "Unmerge every listed package first, then this package could be merged."
			eerror ""
			die "Package can't be installed due to other packages are blocking it."
		fi
	fi
}